import $ from "jquery";
import { setStyle, audio, rad } from '../../utils/other';
import { randomColor } from '../../utils/color';
import { addLoop, overLoopSign, drawLoop } from '../../utils/loop';
const { random, cos, sin, PI } = Math;

const frames = 1000 / 60; // 帧率

const possOfBig = 40; // 烟花加大可能性（%）
const burstValueMix = 70; // 烟花粒子最小数量
const burstValueMax = 100; // 烟花粒子最大数量

const burstMinTime = 500; // 烟花粒子最小停留时间（ms）
const burstMaxTime = 800; // 烟花粒子最大停留时间（ms）
const burstSpeed = 2.6; // 烟花粒子移动速度（px）
const burstGhostTime = 800; // 烟花粒子尾气停留时长（ms）

const possOfCrackle = 15; // 烟花粒子爆裂可能性（%）
const cracklesValue = 3; // 烟花粒子爆裂数量
const crackleTime = 1000; // 烟花爆裂粒子停留时长（ms）

const liftSpeed = 20; // 烟花发射移动速度（px）
const liftGhostTime = 300; // 烟花尾气停留时长（ms）

const starMoveSpeed = 0.001 // 星星移动速度（px）

const canvas = document.createElement("canvas");
const core = canvas.getContext('2d');
const list = [];


function resize() {
    canvas.width = $(window).width();
    canvas.height = $(window).height();
};

function initCanvas() {
    setStyle(canvas, {
        position: "fixed",
        top: "0",
        left: "0",
        zIndex: "-1",
        pointerEvents: "none"
    });
    $(window).on("resize", resize);
    resize();
    document.body.append(canvas);
};


var fpsTimeAni = Date.now();
var fpsTicksAni = 0;
var fpsAni = 0;
function aniFPSListener() {
    fpsTicksAni++;
    if (Date.now() - fpsTimeAni >= 1000) {
        fpsAni = fpsTicksAni;
        fpsTicksAni = 0;
        fpsTimeAni = Date.now();
    }
}

var fpsTimeDraw = Date.now();
var fpsTicksDraw = 0;
var fpsDraw = 0;
function draw() {
    fpsTicksDraw++;
    if (Date.now() - fpsTimeDraw >= 1000) {
        fpsDraw = fpsTicksDraw;
        fpsTicksDraw = 0;
        fpsTimeDraw = Date.now();
    }

    core.clearRect(0, 0, $(window).width(), $(window).height());

    core.textAlign = "center";
    core.textBaseline = "middle";
    const readyToDelete = [];

    for (let opt of list) {
        if (opt == null || opt.del) {
            readyToDelete.push(opt);
        } else {
            core.beginPath();

            core.fillStyle = opt.color;
            core.strokeStyle = opt.color;
            core.globalAlpha = opt.opac < 0 ? 0 : opt.opac;
            if (opt.type == "line") {
                core.lineWidth = opt.size;
                core.moveTo(opt.sx, opt.sy);
                core.lineTo(opt.ex, opt.ey);
                core.stroke();
            } else if (opt.type == "text") {
                core.font = `${opt.size}px sans-serif`;
                core.fillText(opt.text, opt.x, opt.y);
            } else {
                core.arc(opt.x - (opt.size / 2), opt.y - (opt.size / 2), opt.size / 2, 0, PI / 180 * 360);
                core.fill();
            }
            core.closePath();
        };
    };

    for (let v of readyToDelete) {
        const index = list.indexOf(v);
        if (index > -1) list.splice(index, 1);
    };

    core.beginPath();
    core.globalAlpha = 1;
    core.fillStyle = "#fff";
    core.textAlign = "start";
    core.textBaseline = "alphabetic";
    core.font = `10px sans-serif`;
    core.fillText(`Draw: ${fpsDraw}fps`, 5, 15);
    core.fillText(`Animation: ${fpsAni}fps`, 5, 30);
    core.fillText(`Nodes: ${list.length}`, 5, 45);
    core.closePath();
};

// 夜晚天空
function initSky() {
    for (let i = 0; i < 125; i++) star();
}

function star() {
    const target = {
        x: random() * $(window).width(),
        y: random() * $(window).height(),
        size: 0.4,
        color: "#fff",
        opac: 1
    }

    list.push(target);

    addLoop(() => {
        // 绕 poX, poY 绕点运动

        const poX = $(window).width() / 2;
        const poY = $(window).height() * 2;

        const rad = Math.atan2(poY - target.y, poX - target.x);
        const dis = Math.sqrt((poX - target.x) ** 2 + (poY - target.y) ** 2);

        target.x = poX;
        target.y = poY;

        const rad2 = rad - PI - (PI / 180) * starMoveSpeed;
        target.x += cos(rad2) * dis;
        target.y += sin(rad2) * dis;

        if (target.x < 0 || target.y > $(window).height() || target.y < 0) {
            if (Math.random() > 0.5) {
                target.x = $(window).width();
                target.y = random() * $(window).height();
            } else {
                target.x = $(window).width() / 2 + random() * $(window).width() / 2;
                target.y = $(window).height();
            }
        }
    })
}


function crackle(x, y, radius, color) {
    const config = {
        x,
        y,
        size: 0.6,
        color,
        opac: 1
    };

    list.push(config);

    var deg = ~~(-180 + random() * 360);
    var xp = cos(rad(deg)) * radius;
    var yp = sin(rad(deg)) * radius;
    config.x += xp;
    config.y += yp;

    var sed = 1 / (crackleTime / frames);

    addLoop(() => {
        if (config.opac <= 0) {
            config.del = true;
            return overLoopSign();
        }

        config.opac -= sed;
        config.y += 0.05;
    });
};

function burstGhost(x, y, xp, yp, big, color) {
    const config = {
        x,
        y,
        size: (big ? 2.6 : 1.75) / 1.4,
        color,
        opac: 1
    };

    list.push(config);

    xp /= 2;
    yp /= 2;

    var sed = 1 / (burstGhostTime / frames);

    addLoop(() => {
        if (config.opac <= 0) {
            config.del = true;
            return overLoopSign();
        }

        config.opac -= sed;
        config.x += xp;
        config.y += yp + 0.2;
        xp /= 1.08;
        yp /= 1.09;
    })
};

function burst(x, y, big, crackles, color, time) {
    const config = {
        x,
        y,
        size: (big ? 2.6 : 1.75),
        color,
        opac: 1
    };

    list.push(config);

    var speed = burstSpeed;
    var deg = ~~(-180 + random() * 360);
    var xp = cos(rad(deg)) * speed;
    var yp = sin(rad(deg)) * speed;

    if (crackles) {
        setTimeout(() => {
            var size = ~~(2 + random() * cracklesValue);
            var radius = ~~(5 + random() * 13);
            var len = 0;
            let time = 0;

            addLoop(() => {
                time++;
                if (time % 10 != 0) return;

                if (len++ >= size) return overLoopSign();
                crackle(config.x, config.y, radius, color);
            });
        }, time);
    }

    var t = 0;
    var sed = 1 / (time / frames);

    addLoop(() => {
        if (config.opac <= 0) {
            config.del = true;
            return overLoopSign();
        }

        t++;
        if (t >= time / (1000 / 16)) {
            config.opac -= sed;
            xp /= 1.08;
            yp /= 1.09;
        }
        config.x += xp;
        config.y += yp + 0.2;

        if (random() > 0.8) {
            burstGhost(config.x, config.y, xp, yp, big, color)
        }

    })

};

function liftGhost(x, sy, ey, big) {
    const config = {
        type: "line",
        sx: x - (big ? 2.6 : 1.75) / 2,
        ex: x - (big ? 2.6 : 1.75) / 2,
        sy,
        ey,
        size: 0.3,
        color: "#fff",
        opac: 1
    };

    list.push(config);

    var time = liftGhostTime;
    var sed = 1 / (time / frames);
    addLoop(() => {
        if (config.opac <= 0) {
            config.del = true;
            return overLoopSign();
        }

        config.opac -= sed;
    })
};

function lift(x, up, big, crackles, onburst = () => { }) {
    audio(`audio/lift${~~(1 + random() * 3)}.mp3`);

    const config = {
        x,
        y: $(window).height(),
        size: big ? 2.6 : 1.75,
        color: "#fff",
        opac: 1
    };

    list.push(config);

    var ey = $(window).height() - up;
    var last = config.y;
    addLoop(() => {
        config.y -= (config.y - ey) / liftSpeed;

        liftGhost(x, config.y, last, big);
        last = config.y;

        if (config.y - ey <= 8) {
            var time = ~~(burstMinTime + random() * (burstMaxTime - burstMinTime));
            var color = randomColor();

            (onburst || (() => { }))({ cfg: config, color, time });

            audio(`audio/burst${~~(1 + random() * 2)}${big ? "" : "-sm"}.mp3`);

            for (let i = 0; i < ~~(burstValueMix + random() * (burstValueMax - burstValueMix)); i++)
                burst(x, config.y, big, crackles, color, time);

            setTimeout(() => {
                if (crackles) {
                    audio(`audio/crackle${big ? "" : "-sm"}.mp3`);
                }
            }, time);

            config.del = true;
            return overLoopSign();
        }

    })
};

function launch(x, up) {
    const crackles = random() * 100 < possOfCrackle;
    const big = random() * 100 < possOfBig;
    lift(x, up, big, crackles);
};

function launchWithText(x, up, text, col = null) {
    var startingHide = false;
    const crackles = random() * 100 < possOfCrackle;
    lift(x, up, true, crackles, ({ cfg: { x, y }, color, time }) => {
        const config = {
            type: "text",
            text,
            x,
            y: y + 5,
            size: 10,
            color: col ? col : color,
            opac: 1
        };

        list.push(config);

        var xp = 2.5;
        var yp = -0.5;
        var t = 0;
        var sed = 1 / (2000 / frames);

        addLoop(() => {
            if (config.opac <= 0) {
                config.del = true;
                return overLoopSign();
            }

            t++;
            if (t >= 10) {
                if (startingHide) {
                    config.opac -= sed
                }
                xp /= 1.08;
                yp /= 1.2;
            }
            if (startingHide) {
                config.opac -= sed
            }
            config.size += xp;
            config.y += yp;
        });
    });

    return {
        close() { startingHide = true }
    }
};

function init() {
    initCanvas();
    initSky();
    drawLoop(draw);
    addLoop(aniFPSListener);
};

export { init, list, launch, launchWithText };